﻿using System.Linq.Expressions;

namespace Xmart.Expressions.DynamicPredicate;

public class DynamicPredicateOptions
{
    private List<Type> OtherValueTypes { get; }
    private List<Type> NumberTypes { get; }
    private List<Type> ThenCompareTypes { get; }
    public IEnumerable<Type> ValueTypes { get; }
    public IEnumerable<Type> ValueObjectTypes { get; }
    public IEnumerable<Type> PropertyTypes { get; }
    public Dictionary<CompareType, IEnumerable<Type>> CompareTypeSupportPropertyTypes { get; }
    public Dictionary<(Type, string, CompareType), Func<DynamicPredicateItem, Expression>> Replacements { get; }
    public IEnumerable<CompareType> NumberTypeCompareTypes { get; }

    public DynamicPredicateOptions()
    {
        NumberTypes =
        [
            typeof(int),
            typeof(long),
            typeof(float),
            typeof(double),
            typeof(short),
            typeof(uint),
            typeof(ulong),
            typeof(ushort),
            typeof(byte),
            typeof(sbyte),
            typeof(decimal),
            typeof(DateTime),
            typeof(Enum),
        ];

        var supportStringTypes = new[] { typeof(string) };
        var supportBoolTypes = new[] { typeof(bool) };

        ThenCompareTypes = [.. NumberTypes.Union(supportStringTypes)];

        OtherValueTypes = [.. supportStringTypes.Union(supportBoolTypes).Union([typeof(Guid), typeof(Enum)])];
        ValueTypes = NumberTypes.Union(OtherValueTypes);
        ValueObjectTypes = [];
        PropertyTypes = NumberTypes.Union(OtherValueTypes);

        CompareTypeSupportPropertyTypes = new()
        {
            { CompareType.HasValue, PropertyTypes },
            { CompareType.NotHasValue, PropertyTypes },
            { CompareType.Empty, PropertyTypes },
            { CompareType.NotEmpty, PropertyTypes },
            { CompareType.IsTrue, supportBoolTypes },
            { CompareType.IsFalse, supportBoolTypes },
            { CompareType.Equals, ValueTypes },
            { CompareType.NotEquals, ValueTypes },
            { CompareType.GreaterThan, ThenCompareTypes },
            { CompareType.GreaterThanOrEqual, ThenCompareTypes },
            { CompareType.LessThan, ThenCompareTypes },
            { CompareType.LessThanOrEqual, ThenCompareTypes },
            { CompareType.Contains, supportStringTypes },
            { CompareType.NotContains, supportStringTypes },
            { CompareType.StartsWith, supportStringTypes },
            { CompareType.NotStartsWith, supportStringTypes},
            { CompareType.EndsWith, supportStringTypes },
            { CompareType.NotEndsWith, supportStringTypes },
            { CompareType.Between, ThenCompareTypes },
            { CompareType.NotBetween, ThenCompareTypes },
            { CompareType.In, ValueTypes },
            { CompareType.NotIn, ValueTypes },
            //{ CompareType.NearBy, GeoTypes },
            //{ CompareType.NotNearBy, GeoTypes },
            { CompareType.BelongTo, ValueTypes },
            { CompareType.NotBelongTo, ValueTypes },
            { CompareType.RegexMatch, supportStringTypes },
            { CompareType.NotRegexMatch, supportStringTypes },
            { CompareType.HasIntersection, ValueTypes },
            { CompareType.NotHasIntersection, ValueTypes },
            { CompareType.AllIn, ValueTypes },
            { CompareType.AllNotIn, ValueTypes },
            { CompareType.ContainsAll, ValueTypes },
        };
        Replacements = [];
        NumberTypeCompareTypes =
        [
            CompareType.HasValue,
            CompareType.NotHasValue,
            CompareType.Equals,
            CompareType.NotEquals,
            CompareType.GreaterThan,
            CompareType.GreaterThanOrEqual,
            CompareType.LessThan,
            CompareType.LessThanOrEqual,
            CompareType.Between,
            CompareType.NotBetween,
            CompareType.In,
            CompareType.NotIn,
        ];
    }

    public void AddNumberType(Type numberType)
    {
        NumberTypes.Add(numberType);
    }

    //public void SetGeoTypes(params Type[] geoTypes)
    //{
    //    GeoTypes.AddRange(geoTypes);
    //}

    public void AddValueType(Type valueType)
    {
        OtherValueTypes.Add(valueType);
    }

    public void AddValueObjectType(Type valueObjectType)
    {
        if (ValueObjectTypes is not List<Type> list)
        {
            list = [.. ValueObjectTypes];
        }
        list.Add(valueObjectType);
    }

    public void AddReplacement(Type reflectedType, string propertyName, CompareType compareType, Func<DynamicPredicateItem, Expression> func)
    {
        Replacements[(reflectedType, propertyName, compareType)] = func;
    }
}
